package cn.com.dhcc.turbo.service.transport;

import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import java.util.StringTokenizer;

import org.apache.ftpserver.impl.PassivePorts;
import org.apache.log4j.Logger;

import cn.com.dhcc.app.core.service.Log;
import cn.com.dhcc.app.core.service.Log.LOGTYPE;

/**
 * 重写获取被动端口的逻辑
 * 
 * @author joe 2015年11月21日 下午11:58:17
 */
public class TurboPassivePorts extends PassivePorts {

	boolean waitUntilGetPort = false;

	Logger logger = Log.getLogger(LOGTYPE.FTP);

	private String passivePortsString = null;

	private static final int MAX_PORT = 65535;

	private static final Integer MAX_PORT_INTEGER = Integer.valueOf(MAX_PORT);

	private List<Integer> freeList = new ArrayList<Integer>();

	private Set<Integer> usedList = new HashSet<Integer>();

	public TurboPassivePorts(String passivePorts, boolean checkIfBound,
			boolean waitUntilGetPassssivePort) {
		super(passivePorts, checkIfBound);
		waitUntilGetPort = waitUntilGetPassssivePort;
		passivePortsString = passivePorts;
		freeList.addAll(parse(passivePortsString));
	}

	/**
	 * 直到获取到可用端口为止，不失败，原来逻辑是获取不到可用的直接异常
	 */
	@Override
	public synchronized int reserveNextPort() {
		// TODO 待实现根据节点来分配端口，这样中央并不需要所有端口都向单个流域和省开放
		int dataPort = -1;
		if (waitUntilGetPort) {
			int loopTimes = 2;
			Thread currThread = Thread.currentThread();
			while ((dataPort == -1) && (--loopTimes >= 0)
					&& (!currThread.isInterrupted())) {

				dataPort = super.reserveNextPort();

				// no available free port - wait for the release notification
				if (dataPort == -1) {
					try {
						logger.warn("无可用被动端口, 等待其他用户释放");
						wait();
					} catch (InterruptedException ex) {
					}
				}
			}
		} else {
			// 原来获取端口的方式
			dataPort = super.reserveNextPort();
		}
		if (dataPort != -1) {
			usedList.add(dataPort);
		}
		return dataPort;
	}

	/**
	 * 释放端口
	 */
	@Override
	public synchronized void releasePort(int port) {
		// 原来释放端口的方式
		super.releasePort(port);
		notify();
	}

	/**
	 * 解析端口字符串
	 * 
	 * @param portsString
	 * @return
	 */
	private static Set<Integer> parse(final String portsString) {
		Set<Integer> passivePortsList = new HashSet<Integer>();

		boolean inRange = false;
		Integer lastPort = Integer.valueOf(1);
		StringTokenizer st = new StringTokenizer(portsString, ",;-", true);
		while (st.hasMoreTokens()) {
			String token = st.nextToken().trim();

			if (",".equals(token) || ";".equals(token)) {
				if (inRange) {
					fillRange(passivePortsList, lastPort, MAX_PORT_INTEGER);
				}
				// reset state
				lastPort = Integer.valueOf(1);
				inRange = false;
			} else if ("-".equals(token)) {
				inRange = true;
			} else if (token.length() == 0) {
				// ignore whitespace
			} else {
				Integer port = Integer.valueOf(token);
				if (inRange) {
					// add all numbers from last int
					fillRange(passivePortsList, lastPort, port);
					inRange = false;
				}
				addPort(passivePortsList, port);
				lastPort = port;
			}
		}
		if (inRange) {
			fillRange(passivePortsList, lastPort, MAX_PORT_INTEGER);
		}
		return passivePortsList;
	}

	/**
	 * Fill a range of ports
	 */
	private static void fillRange(final Set<Integer> passivePortsList,
			final Integer beginPort, final Integer endPort) {
		for (int i = beginPort; i <= endPort; i++) {
			addPort(passivePortsList, Integer.valueOf(i));
		}
	}

	/**
	 * Add a single port if not already in list
	 */
	private static void addPort(final Set<Integer> passivePortsList,
			final Integer port) {
		passivePortsList.add(port);
	}

	/**
	 * 统计端口使用情况
	 * 
	 * @return
	 */
	public String getPortUsedStr() {
		StringBuilder sb = new StringBuilder();
		if (passivePortsString != null) {
			sb.append("设置的端口范围：").append(passivePortsString);
			if (!freeList.isEmpty()) {
				sb.append("剩余端口:");
				for (Integer port : freeList) {
					sb.append(port);
					sb.append(",");
				}
				// remove the last ,
				sb.deleteCharAt(sb.length() - 1);
			} else {
				sb.append("剩余可用端口:无；");
			}
			if (!usedList.isEmpty()) {
				sb.append("已用端口:");
				for (Integer port : usedList) {
					sb.append(port);
					sb.append(",");
				}
				// remove the last ,
				sb.deleteCharAt(sb.length() - 1).append("。");
			} else {
				sb.append("已用端口:未使用。");
			}
		} else {
			sb.append("端口无限制。");
		}
		return sb.toString();
	}

}
